<?php
declare(strict_types=1);
namespace App\Enums;

use App\Enums\{DefaultCase, Localization};
use ReflectionEnum;
use Exception;

/**
 * enum扩展
 *
 * @date 2023-10-11
 */
trait EnumTrait {
    
    /**
     * 魔术方法, 扩展不存在的静态方法
     * case中name的方法化
     *  eg: Status::Y()获取到Y的value或本身的name
     * 
     * @return void
     * @throws Exception
     */
    public static function __callStatic($method, $args) {
        $enums = array_filter(static::cases(), fn($it)=> $it->name == $method);
        if (!empty($enums)) {
            return reset($enums)->value ?? reset($enums)->name;
        }
        throw new Exception('无效方法 '. static::class .'::'. $method .'()');
    }

    /**
     * 魔术方法, 扩展不存在的方法
     * case中name的扩展方法
     *  eg: Status::Y->label(), 获取到Y的本地化label或本身的name
     *
     * @method string label()
     */
    public function __call($method, $args) {
        if ($method == 'label') {
            return $this->getLocalizationLabel((new ReflectionEnum(static::class))->getCase($this->name));
        }
        throw new Exception('无效方法 '. static::class .'::'. $method .'(), 目前只支持label()');
    }

    private static function getLocalizationLabel($case): string {
        $attr = $case->getAttributes(Localization::class);
        $attr = current($attr) ?: null;
        return $attr ? $attr->getArguments()[0] : $case->getName();  // 为空取name
    }

    /**
     * 获取默认case
     * @see \App\Enums\DefaultCase
     *
     * @return $this
     * @throws Exception
     */
    public static function defaultCase() {
        $refl = new ReflectionEnum(static::class);
        $cases = array_filter($refl->getReflectionConstants(), fn($it)=>$it->getAttributes(DefaultCase::class));
        if (($count = count($cases)) != 1) {
            throw new Exception($count == 0 ? '缺少注解 #[DefaultCase]' : '仅可标记唯一注解 #[DefaultCase]');
        }
        return current($cases)->getValue();
    }

    /**
     * 根据name获取Enum对象
     *  - 扩展from($value)方法
     *
     * @param string $name
     * @return $this
     * @throws Exception
     */
    public static function fromName(string $name) {
        $enums = array_filter(static::cases(), fn($it)=> $it->name == $name);
        if (empty($enums)) {
            throw new Exception('无效Name '. static::class .'::'. $name);
        }
        return reset($enums);
    }

    /**
     * 根据name获取Enum对象
     *  - 扩展tryFrom($value)方法
     *
     * @param string $name
     * @return $this|null
     */
    public static function tryFromName(string $name) {
        $enums = array_filter(static::cases(), fn($it)=> $it->name == $name);
        return reset($enums) ?: null;
    }

    /**
     * 获取所有name
     *
     * @return array
     */
    public static function names(): array {
        return array_column(static::cases(), 'name');
    }

    /**
     * 获取所有value
     *
     * @return array
     */
    public static function values(): array {
        $values = array_column(static::cases(), 'value');
        return !empty($values) ? $values : static::names();
    }

    /**
     * 获取所有case对应的注解本地化标签Localization(label), 未标记Localization默认name
     * @see \App\Enums\Localization
     *
     * @return array 
     */
    public static function labels(): array {
        $refl = new ReflectionEnum(static::class);
        foreach($refl->getReflectionConstants() as $it) {
            $lables[] = static::getLocalizationLabel($it);
        }
        return $lables;
    }

    /**
     * 转换为数组
     *
     * @return array ['Del' => 2]
     */
    public static function toArray(): array {
        return array_combine(static::names(), static::values());
    }

    /**
     * 转换为下拉选择框数据
     *
     * @param boolean $localization 是否本地化
     * @return array [2 => '删除']|[2 => 'Del']
     */
    public static function toSelectArray($localization = true): array {
        return array_combine(static::values(), $localization ? static::labels() : static::names());
    }

}
